package org.dfzt.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.dfzt.common.constant.CacheConstant;
import org.dfzt.common.util.ByteRedisSerializer;
import org.dfzt.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.time.Duration;
import java.util.HashSet;
import java.util.Set;

import static java.util.Collections.singletonMap;

@Configuration
@EnableCaching // 开启缓存支持
public class RedisConfig extends CachingConfigurerSupport {
	@Value("${spring.redis.cluster.nodes:}")
	private String nodes;
	@Value("${isSingleRedis}")
	private boolean isSingleRedis;
	@Value("${spring.redis.cluster.max-idle:}")
	private Integer maxIdle;
	@Value("${spring.redis.cluster.min-idle:}")
	private Integer minIdle;
	@Value("${spring.redis.cluster.max-total:}")
	private Integer maxTotal;
	@Value("${spring.redis.cluster.timeout:}")
	private Long maxWaitMillis;
	@Value("${spring.redis.password}")
	private String redisPassword;
    @Value("${spring.redis.database}")
    private Integer database;
	/**
	 * RedisTemplate配置 json序列化
	 * @param lettuceConnectionFactory
	 * @return
	 */
	@Bean(name = "redisTemplateByJson")
	public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
		// 设置序列化
		Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
		om.enableDefaultTyping(DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);
		// 配置redisTemplate
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(lettuceConnectionFactory);
		RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        // key序列化
		redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash key序列化
		redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

    /**
     * RedisTemplate配置  字符串序列换
     * @param lettuceConnectionFactory
     * @return
     */
    @Bean(name = "redisTemplateByString")
    public RedisTemplate<String, String> redisTemplateByString(LettuceConnectionFactory lettuceConnectionFactory) {
        // 配置redisTemplate
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        // key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
        redisTemplate.setValueSerializer(stringSerializer);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(stringSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * RedisTemplate配置  byte数组序列化
     * @param lettuceConnectionFactory
     * @return
     */
    @Bean(name = "redisTemplateByByte")
    public RedisTemplate<String, byte[]> redisTemplateByByte(LettuceConnectionFactory lettuceConnectionFactory) {
        // 配置redisTemplate
        RedisTemplate<String, byte[]> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        RedisSerializer<?> byteRedisSerializer = new ByteRedisSerializer();
        // key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
        redisTemplate.setValueSerializer(byteRedisSerializer);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(byteRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

	@Bean
	LettuceConnectionFactory lettuceConnectionFactory() {
		// 连接池配置
		GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
		poolConfig.setMaxIdle(maxIdle == null ? 8 : maxIdle);
		poolConfig.setMinIdle(minIdle == null ? 1 : minIdle);
		poolConfig.setMaxTotal(maxTotal == null ? 8 : maxTotal);
		poolConfig.setMaxWaitMillis(maxWaitMillis == null ? 5000L : maxWaitMillis);
		LettucePoolingClientConfiguration lettucePoolingClientConfiguration = LettucePoolingClientConfiguration.builder()
				.poolConfig(poolConfig)
				.build();

		if(isSingleRedis){
			// 单机redis
			RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
			String host = nodes.substring(nodes.indexOf("-")+1);
			redisConfig.setHostName(host.substring(0,host.indexOf(":")));
			redisConfig.setPort(oConvertUtils.getInt(host.substring(host.indexOf(":")+1)));
			redisConfig.setDatabase(database);
			if (redisPassword != null && !"".equals(redisPassword)) {
				redisConfig.setPassword(redisPassword);
			}
			return new LettuceConnectionFactory(redisConfig, lettucePoolingClientConfiguration);
		}else{
			// 集群redis
			RedisClusterConfiguration redisConfig = new RedisClusterConfiguration();
			Set<RedisNode> nodeses = new HashSet<>();
			String[] hostses = nodes.split("-");
			for (String h : hostses) {
				h = h.replaceAll("\\s", "").replaceAll("\n", "");
				if (!"".equals(h)) {
					String host = h.split(":")[0];
					int port = Integer.valueOf(h.split(":")[1]);
					nodeses.add(new RedisNode(host, port));
				}
			}
			redisConfig.setClusterNodes(nodeses);
			// 跨集群执行命令时要遵循的最大重定向数量
			redisConfig.setMaxRedirects(3);
			if (StringUtils.isNotBlank(redisPassword)){
			    redisConfig.setPassword(redisPassword);
            } else {
			    redisConfig.setPassword("");
            }
			return new LettuceConnectionFactory(redisConfig, lettucePoolingClientConfiguration);
		}
	}

	/**
	 * 缓存配置管理器
	 *
	 * @param factory
	 * @return
	 */
	@Bean
	public CacheManager cacheManager(@Qualifier("lettuceConnectionFactory") LettuceConnectionFactory factory) {
		// 配置序列化（缓存默认有效期 6小时）
		RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));
		RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
				.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

		// 以锁写入的方式创建RedisCacheWriter对象
		//RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
		// 创建默认缓存配置对象
		/* 默认配置，设置缓存有效期 1小时*/
		//RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
		/* 自定义配置test:demo 的超时时间为 5分钟*/
		RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration)
				.withInitialCacheConfigurations(singletonMap(CacheConstant.TEST_DEMO_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
				.transactionAware().build();
		return cacheManager;
	}
}
